package com.okaixz.client.wechat;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.okaixz.client.model.dto.WxPayAttachInfo;
import com.okaixz.client.model.vo.WxAppPayResult;
import com.okaixz.client.properties.WeChatProperties;
import com.okaixz.common.client.exception.BusinessException;
import com.okaixz.common.client.exception.CommonErrorCode;
import com.okaixz.common.client.vo.WxAppPayNotify;
import com.okaixz.common.client.vo.WxAppPayParams;
import com.okaixz.common.utils.StringUtils;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;

@Component
@Slf4j
/** AppService使用示例 */
public class WxAppPayService {

    @Autowired
    private WeChatProperties weChatProperties;



    /** 商户API私钥路径 */
    //  public static final String privateKeyPath = "C:\\software\\wxcertutil\\hktf_cert\\apiclient_key.pem";
    /**
     * 商户API私钥路径
     */
//    public static final String privateKeyPath = "C:\\Users\\yxky-077\\Downloads\\WXCertUtil\\cert\\1723726326_20250801_cert\\apiclient_key.pem";
//    public static final String privateKeyPath = "/Volumes/cdz/cert/1723726326_20250801_cert/apiclient_key.pem";

//    /**
//     * 商户API私钥路径
//     */
//    public static String privateKeyPath = "/usr/local/okai-server/apiclient_key.pem";
//
//    /**
//     * 商户证书序列号
//     */
//    public static final String merchantSerialNumber = "6BAEEC6282486325A307B9961587B0840891D707";
//
//    /**
//     * 商户APIV3密钥
//     */
//    public static final String apiV3Key = "GXKcdz198810093635CDZgxk19881003";
    /**
     * 支付通知回调地址测试
     */
//    public static final String notify_url = "https://ux328251nc8.vicp.fun/wx/app_pay_callback";

    /**
     * 支付通知回调地址
     */
//    public static String notify_url = "https://www.hktf1688.cn/api/wx/native_pay_callback";


    /**
     * 商户号
     */
    public static String merchantId;

    /**
     * 商户API私钥路径
     */
    public static String privateKeyPath;

    /**
     * 商户证书序列号
     */
    public static String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    public static String apiV3Key;
    /**
     * 支付通知回调地址测试
     */
    public static String notify_url;

    public static AppService service;

    public WxAppPayService() {

    }

    // Bean 初始化完成后执行（最后执行）
    @PostConstruct
    public void initService() {
        log.info("========初始化微信服务===========properites="+weChatProperties);
        merchantId = weChatProperties.getMerchantId();
        privateKeyPath = weChatProperties.getPrivateKeyPath();
        merchantSerialNumber = weChatProperties.getMerchantSerialNumber();
        apiV3Key = weChatProperties.getApiV3Key();
        notify_url=weChatProperties.getNotifyUrl();

        // 初始化商户配置
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(merchantId)
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();

        // 初始化服务
        service = new AppService.Builder().config(config).build();
    }

    public WxAppPayParams createPrePayParams(String sessionId, String userId, String orderNo, Integer gid, Integer price, String gname,String clientName) {
        try {
            WeChatProperties.WxAppProperites wxAppProperites = weChatProperties.getWxAppInfo().get(clientName);
            if (wxAppProperites == null) {
                throw  new BusinessException(CommonErrorCode.WXAPP_NOT_EXIST);
            }
            String appid = wxAppProperites.getAppid();

            WxPayAttachInfo attachInfo = new WxPayAttachInfo();
            attachInfo.setA(userId);
            attachInfo.setB(gid);
            attachInfo.setC(sessionId);

            PrepayRequest request = new PrepayRequest();
            // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
            request.setAppid(appid);
            request.setMchid(merchantId);
            request.setDescription(gname);
            request.setOutTradeNo(orderNo);
            request.setAttach(JSON.toJSONString(attachInfo));
            request.setNotifyUrl(notify_url);
            Amount amount = new Amount();
            amount.setTotal(price);
            amount.setCurrency("CNY");
            request.setAmount(amount);
            // 调用接口
            PrepayResponse prepay = service.prepay(request);
            String prepayId = prepay.getPrepayId();
            System.out.println(prepay);


            //新建一个StringBuilder对象，并将上述参数拼接在一起，用\n作为分隔符
            StringBuilder stringBuilder = new StringBuilder();
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonceStr = IdUtil.nanoId();
            stringBuilder
                    .append(appid).append("\n")
                    .append(timestamp).append("\n")
                    .append(nonceStr).append("\n")
                    .append(prepayId).append("\n");
            try {
                String sign = generateSignature(stringBuilder.toString(), privateKeyPath);

                WxAppPayParams payParams = new WxAppPayParams();
                payParams.setAppId(appid);
                payParams.setOrderNo(orderNo);
                payParams.setPartnerId(merchantId);
                payParams.setPrepayId(prepayId);
                payParams.setPackageValue("Sign=WXPay");
                payParams.setNonceStr(nonceStr);
                payParams.setTimeStamp(timestamp);
                payParams.setSign(sign);
                return payParams;
            } catch (Exception e) {
                e.printStackTrace();
            }


        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            e.printStackTrace();
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            e.printStackTrace();
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {

    }

    /**
     * 关闭订单
     */
    public static void closeOrder() {
        CloseOrderRequest request = new CloseOrderRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        service.closeOrder(request);
    }

//    /**
//     * APP支付下单
//     */
//    public  PrepayResponse prepay() {
//        WeChatProperties.WxAppProperites wxAppProperites = weChatProperties.getWxAppInfo().get(clientName);
//        if (wxAppProperites == null) {
//            throw  new BusinessException(CommonErrorCode.WXAPP_NOT_EXIST);
//        }
//        String appid = wxAppProperites.getAppid();
//        PrepayRequest request = new PrepayRequest();
//        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
//        request.setAppid(appId);
//        request.setMchid(merchantId);
//        request.setDescription("测试商品描述");
//        request.setOutTradeNo("100001");
//        request.setAttach("自定义数据说明");
//        request.setNotifyUrl(notify_url);
//        Amount amount = new Amount();
//        amount.setTotal(1);
//        amount.setCurrency("CNY");
//        request.setAmount(amount);
//        // 调用接口
//        return service.prepay(request);
//    }

    /**
     * 微信支付订单号查询订单
     */
    public static Transaction queryOrderById() {

        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        return service.queryOrderById(request);
    }

    /**
     * 解析支付回调
     *
     * @param request
     * @param response
     * @return
     */
    public WxAppPayResult processResult(HttpServletRequest request, HttpServletResponse response) {
        Transaction transaction = null;
        WxAppPayNotify wxAppPayNotify = null;
        WxAppPayResult result = new WxAppPayResult();
        JSONObject jsonObject = new JSONObject();
        try {
            // 获取请求体原内容（此时获取的数据是加密的）
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            String reqeustJson = requestBody.toString();
            System.out.println("微信app支付回调：" + reqeustJson);

            // 获取请求携带的数据，构造参数
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(request.getHeader("Wechatpay-Serial")) // 微信支付平台证书的序列号
                    .nonce(request.getHeader("Wechatpay-Nonce")) // 签名中的随机数
                    .signature(request.getHeader("Wechatpay-Signature"))  // 应答的微信支付签名
                    .timestamp(request.getHeader("Wechatpay-Timestamp")) // 签名中的时间戳
                    .body(reqeustJson) // 请求体内容（原始内容，不要解析）
                    .build();

            // 初始化RSAAutoCertificateConfig
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId) // 商户号
                    // API证书路径，路径自己调试，能找到就行（NativeUtil为自定义工具类）
                    .privateKeyFromPath(privateKeyPath)
                    .merchantSerialNumber(merchantSerialNumber) // 证书序列号
                    .apiV3Key(apiV3Key) // APIV3密匙
                    .build();

            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            // 解析为Transaction对象（解密数据）
            try {
                transaction = parser.parse(requestParam, Transaction.class);
                log.info(JSON.toJSONString(transaction));
                Transaction.TradeStateEnum tradeState = transaction.getTradeState();
                if (Transaction.TradeStateEnum.SUCCESS == tradeState) {
                    //支付成功
                    String attachStr = transaction.getAttach();
                    WxPayAttachInfo attachInfo = JSON.parseObject(attachStr, WxPayAttachInfo.class);
                    result.setAttachInfo(attachInfo);

                    result.setTrade_type(transaction.getTradeType().toString());
                    result.setTransaction_id(transaction.getTransactionId());
                    result.setOut_trade_no(transaction.getOutTradeNo());

                    String successTime = transaction.getSuccessTime();
                    if (StringUtils.isNotEmpty(successTime)) {
                        try {
                            LocalDateTime successDateTime = LocalDateTime.parse(successTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
                            result.setPayTime(successDateTime);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    response.setStatus(200);
                    jsonObject.put("code", "SUCCESS");
                    jsonObject.put("message", "成功");
                    result.setSuccess(true);
                } else {
                    //支付失败
                    jsonObject.put("code", "ERROR");
                    jsonObject.put("message", "失败");
                    response.setStatus(401);
                }
            } catch (Exception e) {
                e.printStackTrace();
                response.setStatus(401);
                jsonObject.put("code", "ERROR");
                jsonObject.put("message", "失败");
                result.setSuccess(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatus(500);
            jsonObject.put("code", "ERROR");
            jsonObject.put("message", "失败");
            result.setSuccess(false);
        }
        result.setResult(jsonObject.toString());


        return result;
    }

    /**
     * 商户订单号查询订单
     */
    public WxAppPayResult queryOrderByOutTradeNo(String orderNo) throws RuntimeException {
        WxAppPayResult result = new WxAppPayResult();
        JSONObject jsonObject = new JSONObject();
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        request.setOutTradeNo(orderNo);
        request.setMchid(merchantId);
        Transaction transaction = service.queryOrderByOutTradeNo(request);
        if (transaction == null) {
            throw new BusinessException(CommonErrorCode.ORDER_NOT_EXIST);
        }
        Transaction.TradeStateEnum tradeState = transaction.getTradeState();

        if (Transaction.TradeStateEnum.SUCCESS == tradeState) {
            //支付成功
            String attachStr = transaction.getAttach();
            WxPayAttachInfo attachInfo = JSON.parseObject(attachStr, WxPayAttachInfo.class);
            result.setAttachInfo(attachInfo);

            result.setTrade_type(transaction.getTradeType().toString());
            result.setTransaction_id(transaction.getTransactionId());
            result.setOut_trade_no(transaction.getOutTradeNo());

            String successTime = transaction.getSuccessTime();
            if (StringUtils.isNotEmpty(successTime)) {
                try {
                    LocalDateTime successDateTime = LocalDateTime.parse(successTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
                    result.setPayTime(successDateTime);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            jsonObject.put("code", "SUCCESS");
            jsonObject.put("message", "成功");
            result.setSuccess(true);
        } else {
            //支付失败
            jsonObject.put("code", "ERROR");
            jsonObject.put("message", "失败");
        }

        result.setResult(jsonObject.toString());
        return result;
    }

    // 生成签名的方法
    private String generateSignature(String parms, String privateKeyPath) throws Exception {
        // 读取私钥
        PrivateKey privateKey = readPrivateKey(privateKeyPath);
        // 签名
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(parms.getBytes(StandardCharsets.UTF_8));
        byte[] signed = sign.sign();

        // 将签名转换为Base64字符串
        return Base64.getEncoder().encodeToString(signed);
    }

    // 读取私钥的方法
    private PrivateKey readPrivateKey(String privateKeyPath) throws Exception {
        FileReader fileReader = new FileReader(privateKeyPath);
        PemReader pemReader = new PemReader(fileReader);
        PemObject pemObject = pemReader.readPemObject();
        byte[] content = pemObject.getContent();

        if (pemObject.getType().equals("PRIVATE KEY")) {
            // PKCS#8 format
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(content);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(spec);
        } else if (pemObject.getType().equals("RSA PRIVATE KEY")) {
            // Traditional OpenSSL format
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(content);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            return converter.getPrivateKey(privateKeyInfo);
        } else {
            throw new IllegalArgumentException("Unsupported PEM type: " + pemObject.getType());
        }
    }
}
